﻿// <copyright file="EmgaatsExtension.cs" company="City of Portland, BES-ASM">
// </copyright>
// <summary>EmgaatsExtension class</summary>

namespace BESASM.EMGAATS.UI.ArcMap
{
  using System;
  using System.Collections.Generic;
  using System.IO;
  using System.Text;
  using System.Windows.Forms;
  using BESASM.EMGAATS.BusinessClasses;
  using ESRI.ArcGIS.Carto;
  using ESRI.ArcGIS.esriSystem;
  using ESRI.ArcGIS.Geodatabase;
  using FluentValidation.Results;

  /// <summary>
  /// The EMGAATS Extension for ArcMap
  /// </summary>
  public class EmgaatsExtension : ESRI.ArcGIS.Desktop.AddIns.Extension
  {
    private IActiveViewEvents_Event mapEvents = null;
    private Dictionary<EntityType, int> selectedEntityCounts = new Dictionary<EntityType, int>();

    /// <summary>
    /// Initializes a new instance of the <see cref="EmgaatsExtension"/> class.
    /// </summary>
    public EmgaatsExtension()
    {
      this.ClearSelectedEntityCounts();
    }

    /// <summary>
    /// Gets a shortcut to the EMGAATS Extension
    /// </summary>
    public static EmgaatsExtension Instance { get; private set; }

    /// <summary>
    /// Gets the network manager that manages the connection between network entities and their associated features.
    /// </summary>
    public NetworkManager NetworkManager { get; private set; }

    /// <summary>
    /// Gets a value indicating whether any features representing entities are selected.
    /// </summary>
    public bool EntitiesSelected
    {
      get
      {
        foreach (var entry in this.selectedEntityCounts)
        {
          if (entry.Value > 0)
          {
            return true;
          }
        }

        return false;
      }
    }

    /// <summary>
    /// Adds a layer to the map optionally using the specified predefined layer in the Add-In
    /// </summary>
    /// <param name="featureClass">The feature class to add to the map.</param>
    /// <param name="layerFileName">The name of the layer file to use as a template.</param>
    /// <remarks>The layer file must be stored in the Data folder in the Add-In.</remarks>
    public static void AddLayer(IFeatureClass featureClass, string layerFileName = null)
    {
      if (featureClass == null)
      {
        return;
      }

      EnumLayer layers = new EnumLayer();
      IFeatureLayer featureLayer = null;

      if (!string.IsNullOrEmpty(layerFileName))
      {
        string layerFilePath = GetDataFilePath(layerFileName);
        ILayerFile layerFile = new LayerFileClass();
        if (layerFile.get_IsPresent(layerFilePath) && layerFile.get_IsLayerFile(layerFilePath))
        {
          layerFile.Open(layerFilePath);
          featureLayer = layerFile.Layer as IFeatureLayer;
        }
      }

      if (featureLayer == null)
      {
        featureLayer = new FeatureLayer();
        featureLayer.Name = featureClass.AliasName;
      }

      featureLayer.FeatureClass = featureClass;

      // Add layers and refresh table of contents
      layers.Add(featureLayer);
      ArcMap.Document.FocusMap.AddLayers(layers, true);

      ArcMap.Document.CurrentContentsView.Refresh(null);
    }

    /// <summary>
    /// Returns the number of selected features representing entities of the given type.
    /// </summary>
    /// <param name="entityType">The type of entity.</param>
    /// <returns>The number of selected features representing entities of the given type.</returns>
    public int NumberSelectedEntities(EntityType entityType)
    {
      return this.selectedEntityCounts[entityType];
    }

    /// <summary>
    /// Gets the full path to the file in the unpacked Data folder created by the Add-In
    /// </summary>
    /// <param name="fileName">The base name of the file to get the path for.</param>
    /// <returns>The full path of the specified file.</returns>
    internal static string GetDataFilePath(string fileName)
    {
      string filePath = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
      filePath = System.IO.Path.Combine(filePath, "Data");
      filePath = System.IO.Path.Combine(filePath, fileName);
      return filePath;
    }

    /// <summary>
    /// Adds the network layers to the map
    /// </summary>
    internal static void AddNetworkLayersToMap()
    {
      EmgaatsExtension.AddLayer(EmgaatsExtension.Instance.NetworkManager.NodeFeatureClass);
      EmgaatsExtension.AddLayer(EmgaatsExtension.Instance.NetworkManager.LinkFeatureClass);
      EmgaatsExtension.AddLayer(EmgaatsExtension.Instance.NetworkManager.AreaFeatureClass);
      EmgaatsExtension.AddLayer(EmgaatsExtension.Instance.NetworkManager.DirectorFeatureClass);
    }

    /// <summary>
    /// Opens a model database
    /// </summary>
    /// <param name="workspace">Workspace containing location of model database</param>
    internal static void OpenDatabase(IWorkspace workspace)
    {
      System.Windows.Forms.Cursor.Current = Cursors.WaitCursor;
      try
      {
        ValidationResult validation = EmgaatsExtension.Instance.NetworkManager.LoadNetwork(workspace);
        if (validation != null && validation.HasErrors())
        {
          string message = "Could not load EMGAATS database because it had errors. Would you like to see them?";
          var userResponse = MessageBox.Show(FormsUtility.ApplicationWindow, message, "EMGAATS", MessageBoxButtons.YesNo, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1);
          if (userResponse == DialogResult.Yes)
          {
            FormsUtility.ShowValidation(validation);
          }
        }
        else
        {
          EmgaatsExtension.AddNetworkLayersToMap();

          var network = EmgaatsExtension.Instance.NetworkManager.Network;
          string message = string.Format("Database {0} opened and loaded.", workspace.PathName);
          message += Environment.NewLine;
          message += Environment.NewLine + string.Format("({0} links, {1} nodes, {2} areas, {3} directors, {4} weirs, {7} orifices, {5} cross-sections, {6} infiltrators, {8} storages)", network.Links.Count, network.Nodes.Count, network.Areas.Count, network.Directors.Count, network.Weirs.Count, network.CrossSections.Count, network.Infiltrators.Count, network.Orifices.Count, network.Storages.Count);

          if (validation != null && validation.IsValid)
          {
            FormsUtility.ShowMessage(message);
          }
          else
          {
            message += Environment.NewLine;
            message += Environment.NewLine + "However, there were warnings. Would you like to see them?";
            var userResponse = MessageBox.Show(FormsUtility.ApplicationWindow, message, "EMGAATS", MessageBoxButtons.YesNo, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button1);
            if (userResponse == DialogResult.Yes)
            {
              FormsUtility.ShowValidation(validation);
            }
          }
        }
      }
      finally
      {
        System.Windows.Forms.Cursor.Current = Cursors.Default;
      }
    }

    /// <summary>
    /// Called on startup of the extension by ArcMap
    /// </summary>
    protected override void OnStartup()
    {
      EmgaatsExtension.Instance = this;

      this.NetworkManager = new NetworkManager();

      this.WireEvents();
    }

    /// <summary>
    /// Called on shutdown of the extension by ArcMap
    /// </summary>
    protected override void OnShutdown()
    {
      this.ReleaseEvents();
    }

    private void WireEvents()
    {
      // TODO: Sample document event wiring code. Change as needed
      ArcMap.Events.ActiveViewChanged += this.ArcMap_ActiveViewChanged;
    }

    private void ReleaseEvents()
    {
      ArcMap.Events.ActiveViewChanged -= this.ArcMap_ActiveViewChanged;
    }

    private void ArcMap_ActiveViewChanged()
    {
      if (this.mapEvents != null)
      {
        this.mapEvents.SelectionChanged -= this.Map_SelectionChanged;
        this.mapEvents = null;
      }

      if (ArcMap.Document != null && ArcMap.Document.FocusMap != null)
      {
        this.mapEvents = ArcMap.Document.FocusMap as IActiveViewEvents_Event;
        this.mapEvents.SelectionChanged += this.Map_SelectionChanged;
      }
    }

    private void Map_SelectionChanged()
    {
      this.ClearSelectedEntityCounts();

      if (this.NetworkManager != null && this.NetworkManager.IsLoaded)
      {
        IMap map = this.mapEvents as IMap;

        // Get visible feature layers
        UIDClass uid = new UIDClass();
        uid.Value = "{40A9E885-5533-11d0-98BE-00805F7CED21}";
        IEnumLayer enumLayers = map.get_Layers(uid, true);
        IFeatureLayer entityLayer = enumLayers.Next() as IFeatureLayer;
        while (entityLayer != null)
        {
          if (entityLayer.FeatureClass != null)
          {
            EntityType entityType = this.NetworkManager.GetObjectClassEntityType(entityLayer.FeatureClass);
            if (entityType != EntityType.Unknown)
            {
              this.selectedEntityCounts[entityType] = ((IFeatureSelection)entityLayer).SelectionSet.Count;
            }
          }

          entityLayer = enumLayers.Next() as IFeatureLayer;
        }
      }
    }

    private void ClearSelectedEntityCounts()
    {
      this.selectedEntityCounts.Clear();
      foreach (EntityType entityType in Enum.GetValues(typeof(EntityType)))
      {
        this.selectedEntityCounts[entityType] = 0;
      }
    }
  }
}
